/**
 *    Copyright 2009 Seth Albnaese
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/** $Id: Game.java 60 2009-06-15 08:47:09Z salbanese0080@gmail.com $ */

package org.aquiferserver.server.game;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aquiferserver.common.AquiferMessage;
import org.aquiferserver.common.Player;
import org.aquiferserver.server.model.PlayerCommand;
import org.aquiferserver.server.net.AquiferMessageBroker;

/**
 * @author Seth Albanese
 * @version $Id: Game.java 60 2009-06-15 08:47:09Z salbanese0080@gmail.com $
 */
public abstract class Game implements Runnable {

	protected static final Log logger = LogFactory.getLog( org.aquiferserver.server.game.Game.class );
	
	protected final Map<Integer, Player> players = new ConcurrentHashMap<Integer, Player>();
	protected final ConcurrentLinkedQueue<PlayerCommand> commandQueue = new ConcurrentLinkedQueue<PlayerCommand>();
	
	protected AquiferMessageBroker messageBroker;
	
	private Thread gameThread;
	
	private volatile boolean running;
	private long frameDelay = 20;
		
	public void setMessageBroker( AquiferMessageBroker mb ) { messageBroker = mb; }
	
	protected abstract void doGameLoop();
	
	protected abstract Player createPlayer( Integer pid, Object props );
	public abstract List<Player> getPlayers();
	
	protected abstract void sendRemovedPlayerMessage( Player p );
	public abstract Class getPlayerType();
	@Override
	public void run()
	{		
		logger.debug( "Entering game loop" );
		while( running )
		{
			
			// start game logic
			
			doGameLoop();
			
			// end game logic, start cleanup
			if( players.isEmpty() )
			{
				logger.info( "Game is empty, killing thread" );
				stop();
			}
			
			try
			{				
				Thread.sleep( frameDelay );
			}
			catch ( InterruptedException ie )
			{
				logger.warn( "Game thread interrupted: " + ie.getCause(), ie );
				stop();
			}
		}
		logger.debug( "Exited game loop" );
	}
	
	public boolean isRunning() { return running; }
	
	public void start()
	{
		running = true;
		gameThread = new Thread( this );
		gameThread.start();
	}
	
	public void stop() { running = false; }
	
	public void addPlayer( Integer cid, Object props )
	{				
		if( !players.containsKey( cid ) )
		{
			players.put( cid,  createPlayer( cid, props ) );
		}	
	}
	
	public Player addPlayer( Player p )
	{
		if( !players.containsKey( p.getId() ) )
		{
			players.put( p.getId(), p );
		}
		
		return p;
	}
	
	public void removePlayer( Integer cid )
	{
		logger.info( "Removing player " + cid + " from game" );	
		
		if( players.containsKey( cid ) )
		{
			Player p = players.remove( cid );
			
			if( players.isEmpty() )
			{
				logger.info( "Game is empty, stopping thread" );			
				stop();
			}
			else
			{
				sendRemovedPlayerMessage( p );
			}
		}
		
		
	}
		
	public void addPlayerCommand( PlayerCommand cmd )
	{
		commandQueue.add( cmd );
	}
	
}
